/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package net.worlds.lua;

import java.awt.Color;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import net.worlds.WorldsCLI;
import net.worlds.Main;
import net.worlds.ObjectList;
import net.worlds.TraceResult;
import net.worlds.World;
import net.worlds.lua.metatable.EventMetatable;
import net.worlds.lua.metatable.GameObjectMetatable;
import net.worlds.lua.metatable.TraceResultMetatable;
import net.worlds.object.Script;
import org.jbox2d.common.Vec2D;
import se.krka.kahlua.luaj.compiler.LuaCompiler;
import se.krka.kahlua.vm.LuaClosure;
import se.krka.kahlua.vm.LuaState;
import se.krka.kahlua.vm.LuaTableImpl;


public class ScriptContext {
    private LuaTableImpl gameObjectMetatable;
    private LuaTableImpl objectMetatable;
    private LuaTableImpl eventMetatable;
    private LuaTableImpl colorMetatable;
    private LuaTableImpl vec2DMetatable;
    private LuaTableImpl globalTable;
    private LuaTableImpl traceResultMetatable;
    private LuaState mainState;
    private ExecutorService executor;
    public ScriptContext()
    {
        gameObjectMetatable = new LuaTableImpl();
        gameObjectMetatable.rawset("__tostring", new GameObjectMetatable.GameObjectTostring());
        gameObjectMetatable.rawset("__newindex", new GameObjectMetatable.GameObjectNewIndex());
        gameObjectMetatable.rawset("__index", new GameObjectMetatable.GameObjectIndex());
        eventMetatable = new LuaTableImpl();
        eventMetatable.rawset("__index", new EventMetatable.EventIndex());
        colorMetatable = new LuaTableImpl();
        colorMetatable.rawset("__tostring", new ColorFunctions.ColorTostring());
        colorMetatable.rawset("__eq", new ColorFunctions.ColorEq());
        colorMetatable.rawset("__index", new ColorFunctions.ColorIndex());
        vec2DMetatable = new LuaTableImpl();
        vec2DMetatable.rawset("__tostring", new Vec2DFunctions.Vec2DTostring());
        vec2DMetatable.rawset("__eq", new Vec2DFunctions.Vec2DEq());
        vec2DMetatable.rawset("__add", new Vec2DFunctions.Vec2DAdd());
        vec2DMetatable.rawset("__sub", new Vec2DFunctions.Vec2DSub());
        vec2DMetatable.rawset("__mul", new Vec2DFunctions.Vec2DMul());
        vec2DMetatable.rawset("__div", new Vec2DFunctions.Vec2DDiv());
        vec2DMetatable.rawset("__index", new Vec2DFunctions.Vec2DIndex());
        traceResultMetatable = new LuaTableImpl();
        traceResultMetatable.rawset("__index", new TraceResultMetatable.TraceResultIndex());
        globalTable = new LuaTableImpl();
        if(Main.getGame() != null)
            mainState = newState(Main.getGame().getMainWorld());
        executor = Executors.newCachedThreadPool();
    }
    public void run(Runnable r)
    {
        executor.execute(r);
    }
    public void setGame(World world)
    {
        mainState = newState(world);
    }
    public LuaTableImpl getGameObjectMetatable()
    {
        return gameObjectMetatable;
    }
    public LuaState newState()
    {
        LuaState s = new LuaState();
        s.setClassMetatable(getClass(), getGameObjectMetatable());
        s.setClassMetatable(Event.class, eventMetatable);
        s.setClassMetatable(Color.class, colorMetatable);
        s.setClassMetatable(Vec2D.class, vec2DMetatable);
        s.setClassMetatable(TraceResult.class, traceResultMetatable);
        for(Class c : ObjectList.getAllClasses())
        {
            s.setClassMetatable(c, gameObjectMetatable);
        }
        return s;
    }
    public LuaState newState(World world)
    {
        LuaState s = new LuaState();
        s.getEnvironment().rawset("_G", globalTable);
        if(world != null)
            s.getEnvironment().rawset("game", world.getRootObject());
        else
            s.getEnvironment().rawset("game", Main.getGame().getCurrentWorld().getRootObject());
        s.getEnvironment().rawset("sleep", new GlobalFunctions.Sleep());
        s.getEnvironment().rawset("Vec2D", new GlobalFunctions.Vec2DFunction());
        s.getEnvironment().rawset("Color", new GlobalFunctions.ColorFunction());
        s.getEnvironment().rawset("create", new GlobalFunctions.CreateFunction(world));
        s.getEnvironment().rawset("link", new GlobalFunctions.LinkFunction(s, null));
        s.getEnvironment().rawset("loadstring", new GlobalFunctions.LoadstringFunction());
        s.getEnvironment().rawset("getGame", new GlobalFunctions.GetGameFunction(world));
        s.setClassMetatable(getClass(), getGameObjectMetatable());
        s.setClassMetatable(Event.class, eventMetatable);
        s.setClassMetatable(Color.class, colorMetatable);
        s.setClassMetatable(Vec2D.class, vec2DMetatable);
        s.setClassMetatable(TraceResult.class, traceResultMetatable);
        for(Class c : ObjectList.getAllClasses())
        {
            s.setClassMetatable(c, gameObjectMetatable);
        }
        return s;
    }
    public void call(LuaState s, LuaClosure c)
    {
        s.call(c, null);
    }
    public void call(LuaClosure c)
    {
        call(mainState, c);
    }
    public ScriptThread createScriptThread(Script script, String name, String source)
    {
        LuaState state;
        if(script == null)
            state = newState(Main.getGame().getCurrentWorld());
        else
            state = newState(script.world);
        ScriptThread st = new ScriptThread(script, state, name, source);
        return st;
    }
    public void executeScript(String source)
    {
        try {
            //System.err.println(((GameObject)mainState.getEnvironment().rawget("game")).getID());
            LuaClosure c = LuaCompiler.loadstring(source, "Script", mainState.getEnvironment());
            mainState.call(c, null);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        catch(Exception ex)
        {
            if(ex.getMessage() != null)
                Main.log(ex.getMessage());
            else
                Main.log(ex.toString());
            //if(Main.debug || ex instanceof NullPointerException || ex instanceof ClassCastException || ex instanceof ArrayIndexOutOfBoundsException)
            {
                ex.printStackTrace();
            }
            if(Main.noView)
                WorldsCLI.exit = true;
        }
    }
    public void clearEnvironment()
    {
        mainState.currentThread.environment = new LuaTableImpl();
    }
    public void close()
    {
        executor.shutdownNow();
    }
}
